<?php
/**
 * @package qBase
 * @subpackage base
 */

/**
 * The basic object for qBase all objects must inherit QObject
 * @package qBase
 * @subpackage base
 */
class QObject
{
	/**
	 * The name of the class of this object
	 *
	 * @var string
	 */
	private $_class_name;
	/**
	 * The QObject constructor
	 *
	 */
	public function __construct()
	{
		
	}
	
	public function debug()
	{
		$args = func_get_args();
		call_user_func_array(array("QObject", "debugData"), $args);
	}
	
	public function debugBacktrace($provide_object = false)
	{
		$this->debug($this->getDebugBacktrace($provide_object));
	}
	
	public function getDebugBacktrace($provide_object = false)
	{
		/*
			function  	string  	The current function name. See also __FUNCTION__.
			line 		integer 	The current line number. See also __LINE__.
			file 		string 		The current file name. See also __FILE__.
			class 		string 		The current class name. See also __CLASS__
			object 		object 		The current object.
			type 		string 		The current call type. If a method call, "->" is returned. If a static method call, "::" is returned. If a function call, nothing is returned.
			args 		array 		If inside a function, this lists the functions arguments. If inside an included file, this lists the included file name(s). 
		*/
		
		$trace = debug_backtrace($provide_object);
		$out = "";
		if (!$trace)
		{
			$out .= "NULL";
		}
		else 
		{
			$out .= "<table class='__qBaseDebugBacktrace'>";
			$out .= "<tr><th>Function</th><th>Line</th><th>File</th><th>Class</th><th>Object</th><th>Type</th><th>Args</th></tr>";
			foreach ($trace as $tr)
			{
				if (!isset($tr["function"])) $tr["function"] = "<i>null</i>";
				if (!isset($tr["line"])) $tr["line"] = "<i>null</i>";
				if (!isset($tr["file"])) $tr["file"] = "<i>null</i>";
				if (!isset($tr["class"])) $tr["class"] = "<i>null</i>";
				if (!isset($tr["object"])) $tr["object"] = "<i>null</i>";
				if (!isset($tr["type"])) $tr["type"] = "<i>null</i>";
				if (!isset($tr["args"])) $tr["args"] = "<i>null</i>";
				if (!isset($tr["function"])) $tr["function"] = "<i>null</i>";
					
				if (is_object($tr["object"]))
					$tr["object"] = get_class($tr["object"]);
				
				$out .= "<tr><td>{$tr["function"]}</td><td>{$tr["line"]}</td><td>{$tr["file"]}</td><td>{$tr["class"]}</td><td>{$tr["object"]}</td><td>{$tr["type"]}</td><td>{$tr["args"]}</td></tr>";
			}
			$out .= "</table>";
		}
		
		return $out;
	}
	
	public static function debugData()
	{
		if ((func_num_args() == 1) && is_string(func_get_arg(0)))
			QWebApp::DebugAppend(func_get_arg(0));
		else 
		{
			$args = func_get_args();
			call_user_func_array(array("QWebApp", "DebugVarDump"), $args);
		}
	}
	
	/**
	 * Determies if the specified object_class is a inheriting object of the class parameter or itself
	 * this function acts just like the PHP function is_a , but accepts a string as the first parameter
	 * This function is used usualy before eval for security reasons
	 *
	 * @param string $object_class
	 * @param string $class
	 * @return boolean
	 */
	public static function IsA($object_class, $class)
	{
		// use this to force the load of the classes
		if (!class_exists($object_class))
			throw new Exception("Class $object_class not found");
		// use this to force the load of the classes
		if (!class_exists($class))
			throw new Exception("Class $class not found");
			
		if ($object_class == $class)
			return true;
		return (is_subclass_of($object_class, $class));
	}
	
	/**
	 * Gets the name of the class for this object
	 *
	 * @return string
	 */
	public function getClassName()
	{
		if (!$this->_class_name)
			$this->_class_name = get_class($this);
		return $this->_class_name;
	}
	
	public function allowObjectSerialize()
	{
		// false by default
		return ($this instanceof QIAllowSerialize);
	}
	
	public function __getAllProperties()
	{
		if ($this->allowObjectSerialize())
		{
			$arr = array();
			foreach ($this as $k => $v)
			{
				if (isset($this->{$k}))
					$arr[$k] = $v;
			}
			
			return $arr;
		}
		else 
			return null;
	}
	
	public static $inc = 0;
	
	public function __setAllProperties($props)
	{
		if ($this->allowObjectSerialize())
		{
			foreach ($props as $k => $v)
			{
				$this->{$k} = $v;
				QObject::$inc++;
			}
		}
		else 
		{
			// nothing
		}
	}
	
	public function getSerializedCode()
	{
		$out = "";
		$ref_bag = array();
		$id_pos = 1;
		
		// static properties 
		$this->generateSerializedCode($out, $ref_bag, $id_pos);
		
		return $out;
	}
	
	public function generateSerializedCode(&$out, &$ref_bag, &$id_pos)
	{
		if (!$this->allowObjectSerialize())
			return null;
		$n = $id_pos++;
		$out .= "\$v_{$n} = new ".$this->getClassName()."();\n";
		$ref_bag[$n] = $this;
		
		$props = $this->__getAllProperties();
		if ($props)
		{
			// $p_str = $this->generateSerializedCodeForArray($props, $out, $ref_bag, $id_pos);
			foreach ($props as $k => $v)
			{
				if ($k == "_class_name")
					continue;
				$out .= "\$v_{$n}->{$k} = ".$this->generateSerializedCodeForVar($v, $out, $ref_bag, $id_pos).";\n"; //__setAllProperties({$p_str});\n";
			}
		}
		
		return $n;
	}
	
	public function generateSerializedCodeForArray($array, &$out, &$ref_bag, &$id_pos)
	{
		$p_str = "array(";
		
		$p = 0;
		foreach ($array as $k => $v)
		{
			if (is_resource($v))
				continue;
			
			if ($p > 0)
				$p_str .= ",";
			
			if (is_string($k))
				$p_str .= $this->__escapeString($k)."=>";
			else 
				$p_str .= $k."=>";
			
			$p_str .= $this->generateSerializedCodeForVar($v, $out, $ref_bag, $id_pos);
			
			$p++;
		}
		$p_str .= ")";
		
		return $p_str;
	}
	
	public function generateSerializedCodeForVar($v, &$out, &$ref_bag, &$id_pos)
	{
		if (is_null($v))
			$p_str = "null";
		else if (is_bool($v))
			$p_str = ($v ? "true" : "false");
		else if (is_string($v))
			$p_str = $this->__escapeString($v);
		else if (is_array($v))
			$p_str = $this->generateSerializedCodeForArray($v, $out, $ref_bag, $id_pos);
		else if (is_object($v))
		{
			if (($v instanceof QObject) || ($v instanceof QIAllowSerialize))
			{
				// and now the tricky part
				$found = false;
				$v_id = null;
				foreach ($ref_bag as $ref_id => $obj)
				{
					if ($obj === $v)
					{
						$found = true;
						$v_id = $ref_id;
						break;
					}
				}
				
				if (!$found)
					$v_id = $v->generateSerializedCode($out, $ref_bag, $id_pos);
				if (is_null($v_id))
					$p_str = "null";
				else
					$p_str = "\$v_{$v_id}";
			}
			else 
				$p_str = "null";
		}
		else // numeric
			$p_str = $v;
			
		return $p_str;
	}
	
	function __escapeString($str)
	{
		if ($str !== null)
			return "'".str_replace(array('\\','\''),array('\\\\','\\\''), $str)."'";
		else 
			return "null";
	}
	
}

?>